<template>
	<view class="rate-box" :class="[{ animation }, containerClass]" @touchmove="ontouchmove" @touchend="touchMoving = false">
		<view
			v-for="(val, i) in list"
			:key="val"
			class="rate"
			:style="{ fontSize, paddingLeft: i !== 0 ? rateMargin : 0, paddingRight: i < list.length - 1 ? rateMargin : 0 }"
			:class="[
				{ scale: !disabled && val <= rateValue && animation && touchMoving, 'color-base-text': val <= rateValue, defaultColor: val > rateValue },
				`rate-${i}`,
				rateClass
			]"
			:data-val="val"
			@click="onItemClick"
		>
			<text class="iconfont icon-star"></text>
		</view>
	</view>
</template>

<script>
import { getClientRect } from './common';

export default {
	name: 'sx-rate',
	props: {
		// 当前值
		value: {
			type: [Number, String]
		},
		// 最大星星数量
		max: {
			type: Number,
			default: 5
		},
		// 禁用
		disabled: {
			type: Boolean,
			default: false
		},
		// 动画效果
		animation: {
			type: Boolean,
			default: true
		},
		// 默认星星颜色
		defaultColor: {
			type: String,
			default: '#ccc'
		},
		// 滑选后星星颜色
		activeColor: {
			type: String
			// default: '#FFB700'
		},
		// 星星大小
		fontSize: {
			type: String,
			default: 'inherit'
		},
		// 星星间距
		margin: {
			type: String,
			default: ''
		},
		// 自定义类名-容器
		containerClass: {
			type: String,
			default: ''
		},
		// 自定义类名-星星
		rateClass: {
			type: String,
			default: ''
		},
		index: {
			// 如果页面中存在多个该组件，通过该属性区分
			type: [Number, String]
		}
	},
	data() {
		return {
			rateValue: 0,
			touchMoving: false,
			startX: [],
			startW: 30
		};
	},
	computed: {
		list() {
			return [...new Array(this.max)].map((_, i) => i + 1);
		},
		rateMargin() {
			let margin = this.margin;
			if (!margin) return 0;

			switch (typeof margin) {
				case 'number':
					margin += 'px';
				case 'string':
					break;
				default:
					return 0;
			}

			let reg = /^(\d+)([^\d]*)/;
			let result = reg.exec(margin);
			if (!result) return 0;

			let [_, num, unit] = result;
			return num / 2 + unit;
		}
	},
	watch: {
		value: {
			handler(val) {
				this.rateValue = val;
			},
			immediate: true
		}
	},
	methods: {
		// 计算星星位置
		async initStartX() {
			let { max } = this;
			this.startX = [];

			for (let i = 0; i < max; i++) {
				let selector = `.rate-${i}`;
				let { left, width } = await getClientRect(selector, this);

				this.startX.push(left);
				this.startW = width;
			}
		},

		/**
		 * 手指滑动事件回调
		 * https://github.com/sunxi1997/uni-app-sx-rate/pull/1
		 * 原本的触摸处理在自定了样式后可能会出现bug, 已解决
		 */
		async ontouchmove(e) {
			if (!this.touchMoving) {
				this.touchMoving = true;
				// 开始手指滑动时重新计算星星位置,防止星星位置意外变化
				await this.initStartX();
			}

			let { startX, startW, max } = this;
			let { touches } = e;

			// 触摸焦点停留的位置
			let { pageX } = touches[touches.length - 1];

			// 超出最左边, 0 星
			if (pageX <= startX[0]) return this.toggle(0);
			// 刚好在第一颗星
			else if (pageX <= startX[0] + startW) return this.toggle(1);
			// 超出最右边, 最大星
			else if (pageX >= startX[max - 1]) return this.toggle(max);

			//计算星星停留的位置
			let startXHash = startX.concat(pageX).sort((a, b) => a - b);
			this.toggle(startXHash.indexOf(pageX));
		},
		// 点击回调
		onItemClick(e) {
			let { val } = e.currentTarget.dataset;
			this.toggle(val);
		},
		// 修改值
		toggle(val) {
			let { disabled } = this;
			if (disabled) return;
			if (this.rateValue !== val) {
				this.rateValue = val;
				this.$emit('update:value', val);
				let data = {
					index: this.index,
					value: val
				};
				this.$emit('change', data);
			}
		}
	},
	mounted() {}
};
</script>

<style scoped>
@import './sx-rate/iconfont.css';
</style>

<style lang="scss">
.rate-box {
	min-height: 1.4em;
	display: flex;
	align-items: center;
}

.rate {
	display: inline-flex;
	justify-content: center;
	align-items: center;
	width: 1.2em;
	transition: all 0.15s linear;
}

.rate.scale {
	transform: scale(1.1);
}

.defaultColor {
	color: #ccc !important;
}
.activeColor {
}
</style>
